#ifndef __WIZ_GEOMETRY_CLIPPING_PLANES_HPP__SHANHAOBO_19800429__
#define __WIZ_GEOMETRY_CLIPPING_PLANES_HPP__SHANHAOBO_19800429__

#include "../../WizAlgebra/Include/Linear/Matrix44/WizAlgebraMatrix44.hpp"
#include "./WizGeometryPlane.hpp"

namespace Wiz
{
    namespace ClippingPlanes
    {
        enum ePlane
        {
            ePlaneNear   = 0,
            ePlaneFar    = 1,
            ePlaneLeft   = 2,
            ePlaneRight  = 3,
            ePlaneTop    = 4,
            ePlaneBottom = 5,
            ePlaneNum    = 6
        };
        ////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////
        template<typename Matrix44T>
        struct Type
        {
        public:
            ////////////////////////////////////////////////////////////////
            typedef Matrix44T								tMatrix44;
            typedef typename tMatrix44::tElement            tElement;
            typedef Type<tElement>							tThis;
            typedef ::Wiz::Plane::Type<tElement>		tPlane;
            typedef ::Wiz::Vector3::Type<tElement>	tVector3;
            typedef ::Wiz::Vector3::Type<tElement>	tLocation3;
            ////////////////////////////////////////////////////////////////
        protected:
            ////////////////////////////////////////////////////////////////
            tPlane            m_Planes[::Wiz::ClippingPlanes::ePlaneNum];
            ////////////////////////////////////////////////////////////////
        public:
            ::Wiz::Bool::Type IsIn(const tLocation3& loc3)
            {
                ::Wiz::UInt::Type i;
                for (i = 0; i < ::Wiz::ClippingPlanes::ePlaneNum; i++)
                {
                    if (this->m_Planes[i].GetSide(loc3) == ::Wiz::Plane::eNegative)
                    {
                        return ::Wiz::Bool::False;
                    }
                }

                return ::Wiz::Bool::True;
            }
            ::Wiz::Bool::Type IsIn(const tLocation3& loc3, const tVector3& sz3)
            {
                ::Wiz::UInt::Type i;
                for (i = 0; i < ::Wiz::ClippingPlanes::ePlaneNum; i++)
                {
                    if (this->m_Planes[i].GetSide(loc3, sz3) == ::Wiz::Plane::eNegative)
                    {
                        return ::Wiz::Bool::False;
                    }
                }

                return ::Wiz::Bool::True;
            }
        protected:
            WIZ_INLINE ::Wiz::Void::Type Build(const tMatrix44& Combo)
            {
                tElement Len;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneLeft].m_Normal.x = Combo.e03 + Combo.e00;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneLeft].m_Normal.y = Combo.e13 + Combo.e10;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneLeft].m_Normal.z = Combo.e23 + Combo.e20;
                Len = this->m_Planes[::Wiz::ClippingPlanes::ePlaneLeft].m_Normal.Normalize();
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneLeft].m_Constant = (Combo.e33 + Combo.e30) / Len;

                this->m_Planes[::Wiz::ClippingPlanes::ePlaneRight].m_Normal.x = Combo.e03 - Combo.e00;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneRight].m_Normal.y = Combo.e13 - Combo.e10;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneRight].m_Normal.z = Combo.e23 - Combo.e20;
                Len = this->m_Planes[::Wiz::ClippingPlanes::ePlaneRight].m_Normal.Normalize();
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneRight].m_Constant = (Combo.e33 - Combo.e30) / Len;

                this->m_Planes[::Wiz::ClippingPlanes::ePlaneTop].m_Normal.x = Combo.e03 - Combo.e01;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneTop].m_Normal.y = Combo.e13 - Combo.e11;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneTop].m_Normal.z = Combo.e23 - Combo.e21;
                Len = this->m_Planes[::Wiz::ClippingPlanes::ePlaneTop].m_Normal.Normalize();
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneTop].m_Constant = (Combo.e33 - Combo.e31) / Len;

                this->m_Planes[::Wiz::ClippingPlanes::ePlaneBottom].m_Normal.x = Combo.e03 + Combo.e01;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneBottom].m_Normal.y = Combo.e13 + Combo.e11;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneBottom].m_Normal.z = Combo.e23 + Combo.e21;
                Len = this->m_Planes[::Wiz::ClippingPlanes::ePlaneBottom].m_Normal.Normalize();
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneBottom].m_Constant = (Combo.e33 + Combo.e31) / Len;

                this->m_Planes[::Wiz::ClippingPlanes::ePlaneNear].m_Normal.x = Combo.e03 + Combo.e02;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneNear].m_Normal.y = Combo.e13 + Combo.e12;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneNear].m_Normal.z = Combo.e23 + Combo.e22;
                Len = this->m_Planes[::Wiz::ClippingPlanes::ePlaneNear].m_Normal.Normalize();
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneNear].m_Constant = (Combo.e33 + Combo.e32) / Len;

                this->m_Planes[::Wiz::ClippingPlanes::ePlaneFar].m_Normal.x = Combo.e03 - Combo.e02;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneFar].m_Normal.y = Combo.e13 - Combo.e12;
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneFar].m_Normal.z = Combo.e23 - Combo.e22;
                Len = this->m_Planes[::Wiz::ClippingPlanes::ePlaneFar].m_Normal.Normalize();
                this->m_Planes[::Wiz::ClippingPlanes::ePlaneFar].m_Constant = (Combo.e33 - Combo.e32) / Len;
            }
        public:
            ////////////////////////////////////////////////////////////////
            ::Wiz::Void::Type BuildInViewSpace(const tMatrix44& Project)
            {
                this->Build(Project);
            }
            ////////////////////////////////////////////////////////////////
            ::Wiz::Void::Type BuildInWorldSpace(const tMatrix44& View, const tMatrix44& Project)
            {
                tMatrix44 Combo = View * Project;

                this->Build(Combo);
            }
            ////////////////////////////////////////////////////////////////
            ::Wiz::Void::Type BuildInObjectSpace(const tMatrix44& WorldMat, const tMatrix44& View, const tMatrix44& Project)
            {
                tMatrix44 Combo = WorldMat * View * Project;

                this->Build(Combo);
            }
        }; /// end of class Type
        ////////////////////////////////////////////////////////////////////
    } /// end of namespace ClippingPlanes
} /// end of namespace Wiz

#endif /*__WIZ_GEOMETRY_CLIPPING_PLANES_HPP__SHANHAOBO_19800429__*/
